Goal: Do a PCA plot using Irene’s SNPs to look for relatedness among plants. Also figure out loading (which genes contribute most)

Snp calling with freebayes

First, need to call SNPs using all BAM files at once:

working in directory 2019/IreneSnps on whitney

first, sort bams

for f in SNPanalysis/*LT*rmdup.bam
    do 
       newname=`basename $f .bam`_sort.bam
       samtools sort -o $newname --reference SNPanalysis/Pinsaporeference1 $f 
    done

next, assign read groups to bams

input=""

for f in `ls *sort.bam`
   do
      rg=`basename $f _rmdup_sort.bam`
      input="$input -b $f -r $rg -s $rg"
  done

echo $input

bamaddrg $input > LT_rmdup_sort_combined.bam

samtools index LT_rmdup_sort_combined.bam
freebayes -f SNPanalysis/Pinsaporeference1 --no-indels --no-mnps --no-complex LT_rmdup_sort_combined.bam > LT.vcf &

try parallel

ulimit -n 4000
/usr/local/stow/freebayes/scripts/fasta_generate_regions.py Pinsaporeference1.fai 100000 > regions
./freebayes-parallel regions 8 -f Pinsaporeference1 --no-indels --no-mnps --no-complex LT_rmdup_sort_combined.bam > LT.vcf 

(note: I edited the freebayes-parallel script so that it would work…)

Freeybayes parallel takes about 12 hours

scp whitney.plb.ucdavis.edu:2019/IreneSnps/LT.vcf.gz ../input/

now analyze vcf in R

library(tidyverse)
Registered S3 method overwritten by 'dplyr':
  method           from
  print.rowwise_df     
── Attaching packages ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.2.1 ──
✔ ggplot2 3.2.1     ✔ purrr   0.3.2
✔ tibble  2.1.3     ✔ dplyr   0.8.3
✔ tidyr   1.0.0     ✔ stringr 1.4.0
✔ readr   1.3.1     ✔ forcats 0.4.0
── Conflicts ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
library(ggrepel)

get the vcf header

vcf.header <- system("zgrep '#C' ../input/LT.vcf.gz",intern = TRUE) 
vcf.header
[1] "#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\t38LTR\t42LTR\t42LTRR\t43LTR\t43LTRR\t49LTWR\t49LTWRR\t95LTWR\t95LTWRR\t99LTWR"
vcf.header <- vcf.header %>% 
  str_replace("#","") %>% #get rid of the pound sign
  str_split(pattern = "\t") %>% #split on the tabs
  magrittr::extract2(1)
vcf.header
 [1] "CHROM"   "POS"     "ID"      "REF"     "ALT"     "QUAL"    "FILTER"  "INFO"    "FORMAT"  "38LTR"   "42LTR"   "42LTRR"  "43LTR"   "43LTRR"  "49LTWR"  "49LTWRR"
[17] "95LTWR"  "95LTWRR" "99LTWR" 

get the data

snps <- read_tsv("../input/LT.vcf.gz", na = c("","NA","."),comment="#",col_names = vcf.header) %>%
  select(-ID, -FILTER) # these are empty columns
restarting interrupted promise evaluationParsed with column specification:
cols(
  CHROM = col_character(),
  POS = col_double(),
  ID = col_logical(),
  REF = col_character(),
  ALT = col_character(),
  QUAL = col_double(),
  FILTER = col_logical(),
  INFO = col_character(),
  FORMAT = col_character(),
  `38LTR` = col_character(),
  `42LTR` = col_character(),
  `42LTRR` = col_character(),
  `43LTR` = col_character(),
  `43LTRR` = col_character(),
  `49LTWR` = col_character(),
  `49LTWRR` = col_character(),
  `95LTWR` = col_character(),
  `95LTWRR` = col_character(),
  `99LTWR` = col_character()
)
snps

filter to keep snps where there is data from all samples

snps <- snps %>% 
  filter({select(., matches("[0-9]")) %>% complete.cases() })
snps
snps <- snps %>% 
  mutate(TOTAL_DEPTH= {str_extract(INFO, "DP=[0-9]*") %>% 
      str_remove("DP=") %>%
      as.numeric() }
  ) %>%
  filter(QUAL >=100,
         nchar(ALT)==1,
         TOTAL_DEPTH > quantile(TOTAL_DEPTH, 0.05),
         TOTAL_DEPTH < quantile(TOTAL_DEPTH, 0.95))
snps

unpack the information differnet samples:

samples <- colnames(snps) %>% str_subset("^[0-9]")

for (s in samples) {
snps <- snps %>%
  separate(!!s, into=paste(s,c("gt","tot.depth","allele.depth","ref.depth","ref.qual","alt.depth","alt.qual","gt.lik"),sep="_"),
           sep=":", convert = TRUE)
}
snps

For the PCA we only need the genotype info

gts <- snps %>%
  select(CHROM, POS, ends_with("_gt"))
gts

remove the 38LTR gample

gts <- gts %>% select(-`38LTR_gt`)

convert this to numeric

geno.numeric <- gts %>%
  select(-CHROM, -POS) %>%
  lapply(factor) %>% # convert charcters to "factors", where each category is internally represented as a number
  as.data.frame() %>% # reformat
  data.matrix() %>%#  convert to numeric
  t() 

colnames(geno.numeric) <- str_c(gts$CHROM, "_", gts$POS)

head(geno.numeric[,1:5],10)
            GCZN01000007.1_2815 GCZN01000007.1_2834 GCZN01000007.1_2865 GCZN01000007.1_2881 GCZN01000007.1_2970
X42LTR_gt                     2                   3                   2                   2                   2
X42LTRR_gt                    2                   2                   2                   2                   1
X43LTR_gt                     2                   2                   2                   2                   2
X43LTRR_gt                    2                   2                   2                   2                   2
X49LTWR_gt                    2                   2                   2                   2                   2
X49LTWRR_gt                   3                   3                   3                   3                   1
X95LTWR_gt                    2                   3                   3                   2                   2
X95LTWRR_gt                   2                   3                   3                   2                   2
X99LTWR_gt                    2                   2                   2                   2                   2
dim(geno.numeric)
[1]      9 134421
dim(gts)
[1] 134421     11

get the principal components

pca <- prcomp(geno.numeric)
summary(pca)
Importance of components:
                           PC1     PC2     PC3     PC4     PC5      PC6      PC7      PC8       PC9
Standard deviation     78.9711 75.4129 60.8894 54.9581 52.3780 45.30719 43.88667 42.97555 1.288e-11
Proportion of Variance  0.2291  0.2089  0.1362  0.1110  0.1008  0.07541  0.07076  0.06785 0.000e+00
Cumulative Proportion   0.2291  0.4380  0.5742  0.6852  0.7860  0.86139  0.93215  1.00000 1.000e+00

plot it

plot.data <- pca$x %>%
  as.data.frame %>%
  rownames_to_column("sample") %>%
  mutate(response=str_extract(sample, "(LTR|LTWR)")) %>%
  gather(key="component", value="value",PC2:PC9)
  
plots <- map(sort(unique(plot.data$component)), function(x) {
  plot.data %>%
    filter(component==x) %>%
    ggplot(aes(x=PC1, y= value, label=sample, color=response)) +
      geom_point() + ylab(x)
  }
  )

plots  
[[1]]

[[2]]

[[3]]

[[4]]

[[5]]

[[6]]

[[7]]

[[8]]

snps that contribute the most to PC1:

loadings <- as.data.frame(pca$rotation) %>%
  rownames_to_column("snp") %>%
  select(PC1, snp) %>%
  arrange(desc(abs(PC1)))
loadings
contributions <- loadings %>%
  separate(snp, into=c("contig", "pos"), sep="_") %>%
  group_by(contig) %>%
  summarize(abs.contribution = abs(sum(PC1)),
            contribution = sum(PC1),
            number_of_snps = n()) %>%
  arrange(desc(abs.contribution))
contributions

bring in seq lengths

lengths <- read_csv("../input/Pinsaporeference1_lengths.csv", col_names = c("contig", "length"), skip=1) %>%
  mutate(contig = str_remove(contig, " .*"))
Parsed with column specification:
cols(
  contig = col_character(),
  length = col_double()
)
lengths
contributions <- contributions %>% 
  left_join(lengths) %>%
  mutate(snps_per_100bp = round(number_of_snps / length * 100, 2)) %>%
  select(contig, contribution, length, number_of_snps, snps_per_100bp)
Joining, by = "contig"
contributions
write_csv(contributions, "../output/gene_contributions.csv.gz")

make plot of PC1 vs PC2 for paper

pc1pc2 <- pca$x %>%
  as.data.frame() %>%
  rownames_to_column("ID") %>%
  select(ID, PC1, PC2) %>%
  mutate(ID={str_replace(ID, "W", "N") %>%
       str_replace("RR", "R2") %>%
      str_remove_all("(X|_gt)") },
    response=ifelse(str_detect(ID,"N"), "no recovery", "recovery"))
pc1pc2
pc1pc2 %>%
  ggplot(aes(x=PC1, y = PC2, label=ID, color=response)) +
  geom_point() +
  geom_text_repel(show.legend=FALSE, direction="y")
ggsave("../output/PCA.pdf")
Saving 7.29 x 4.51 in image

Create a list of GCZN01054158.1 SNPs

GCZN01054158.1.loadings <- loadings %>%
  filter(str_detect(snp, fixed("GCZN01054158.1"))) %>%
  separate(snp,into = c("contig", "position"), sep="_",convert = TRUE) %>%
  arrange(position)
GCZN01054158.1.loadings
GCZN01054158.snpinfo <- left_join(GCZN01054158.1.loadings, snps, by=c("contig" = "CHROM", "position" = "POS"))
GCZN01054158.snpinfo
write_csv(GCZN01054158.snpinfo, "../output/GCZN01054158.snpinfo.csv")

Create a list of SNPs in all genes with fixed differences

get_snps <- function(contig, loadings=loadings) {
  loadings %>%
  filter(str_detect(snp, fixed("GCZN01054158.1"))) %>%
  separate(snp,into = c("contig", "position"), sep="_",convert = TRUE) %>%
  arrange(position)
GCZN01054158.1.loadings
GCZN01054158.snpinfo <- left_join(GCZN01054158.1.loadings, snps, by=c("contig" = "CHROM", "position" = "POS"))
GCZN01054158.snpinfo
}
fixed_genes_snps <- loadings %>% 
  separate(snp,into = c("contig", "position"), sep="_",convert = TRUE, remove = FALSE) %>%
  semi_join(fixed_genes, by="contig") %>%
  left_join(snps, by=c("contig" = "CHROM", "position" = "POS")) %>%
  arrange(contig, position)
write_csv(fixed_genes_snps, "../output/fixed_genes_snps.csv")
LS0tCnRpdGxlOiAiUENBIGFuYWx5c2lzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpHb2FsOiBEbyBhIFBDQSBwbG90IHVzaW5nIElyZW5lJ3MgU05QcyB0byBsb29rIGZvciByZWxhdGVkbmVzcyBhbW9uZyBwbGFudHMuICBBbHNvIGZpZ3VyZSBvdXQgbG9hZGluZyAod2hpY2ggZ2VuZXMgY29udHJpYnV0ZSBtb3N0KQoKCiMjIFNucCBjYWxsaW5nIHdpdGggZnJlZWJheWVzCgoKRmlyc3QsIG5lZWQgdG8gY2FsbCBTTlBzIHVzaW5nIGFsbCBCQU0gZmlsZXMgYXQgb25jZToKCndvcmtpbmcgaW4gZGlyZWN0b3J5IGAyMDE5L0lyZW5lU25wc2Agb24gd2hpdG5leQoKZmlyc3QsIHNvcnQgYmFtcwoKYGBge3IsIGVuZ2luZT0nYmFzaCcsIGV2YWw9RkFMU0V9CmZvciBmIGluIFNOUGFuYWx5c2lzLypMVCpybWR1cC5iYW0KICAgIGRvIAogICAgICAgbmV3bmFtZT1gYmFzZW5hbWUgJGYgLmJhbWBfc29ydC5iYW0KICAgICAgIHNhbXRvb2xzIHNvcnQgLW8gJG5ld25hbWUgLS1yZWZlcmVuY2UgU05QYW5hbHlzaXMvUGluc2Fwb3JlZmVyZW5jZTEgJGYgCiAgICBkb25lCmBgYAoKCm5leHQsIGFzc2lnbiByZWFkIGdyb3VwcyB0byBiYW1zCgpgYGB7ciwgZW5naW5lPSdiYXNoJywgZXZhbD1GQUxTRX0KaW5wdXQ9IiIKCmZvciBmIGluIGBscyAqc29ydC5iYW1gCiAgIGRvCiAgICAgIHJnPWBiYXNlbmFtZSAkZiBfcm1kdXBfc29ydC5iYW1gCiAgICAgIGlucHV0PSIkaW5wdXQgLWIgJGYgLXIgJHJnIC1zICRyZyIKICBkb25lCgplY2hvICRpbnB1dAoKYmFtYWRkcmcgJGlucHV0ID4gTFRfcm1kdXBfc29ydF9jb21iaW5lZC5iYW0KCnNhbXRvb2xzIGluZGV4IExUX3JtZHVwX3NvcnRfY29tYmluZWQuYmFtCmBgYAoKCmBgYHtyLCBlbmdpbmU9J2Jhc2gnLCBldmFsPUZBTFNFfQpmcmVlYmF5ZXMgLWYgU05QYW5hbHlzaXMvUGluc2Fwb3JlZmVyZW5jZTEgLS1uby1pbmRlbHMgLS1uby1tbnBzIC0tbm8tY29tcGxleCBMVF9ybWR1cF9zb3J0X2NvbWJpbmVkLmJhbSA+IExULnZjZiAmCmBgYAoKdHJ5IHBhcmFsbGVsCmBgYHtyLCBlbmdpbmU9J2Jhc2gnLCBldmFsPUZBTFNFfQp1bGltaXQgLW4gNDAwMAovdXNyL2xvY2FsL3N0b3cvZnJlZWJheWVzL3NjcmlwdHMvZmFzdGFfZ2VuZXJhdGVfcmVnaW9ucy5weSBQaW5zYXBvcmVmZXJlbmNlMS5mYWkgMTAwMDAwID4gcmVnaW9ucwouL2ZyZWViYXllcy1wYXJhbGxlbCByZWdpb25zIDggLWYgUGluc2Fwb3JlZmVyZW5jZTEgLS1uby1pbmRlbHMgLS1uby1tbnBzIC0tbm8tY29tcGxleCBMVF9ybWR1cF9zb3J0X2NvbWJpbmVkLmJhbSA+IExULnZjZiAKYGBgCgoobm90ZTogSSBlZGl0ZWQgdGhlIGZyZWViYXllcy1wYXJhbGxlbCBzY3JpcHQgc28gdGhhdCBpdCB3b3VsZCB3b3JrLi4uKQoKRnJlZXliYXllcyBwYXJhbGxlbCB0YWtlcyBhYm91dCAxMiBob3VycwoKYGBge3IsIGVuZ2luZT0nYmFzaCcsIGV2YWw9RkFMU0V9CnNjcCB3aGl0bmV5LnBsYi51Y2RhdmlzLmVkdToyMDE5L0lyZW5lU25wcy9MVC52Y2YuZ3ogLi4vaW5wdXQvCmBgYAoKIyMgbm93IGFuYWx5emUgdmNmIGluIFIKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ3JlcGVsKQpgYGAKCmdldCB0aGUgdmNmIGhlYWRlcgpgYGB7cn0KdmNmLmhlYWRlciA8LSBzeXN0ZW0oInpncmVwICcjQycgLi4vaW5wdXQvTFQudmNmLmd6IixpbnRlcm4gPSBUUlVFKSAKdmNmLmhlYWRlcgp2Y2YuaGVhZGVyIDwtIHZjZi5oZWFkZXIgJT4lIAogIHN0cl9yZXBsYWNlKCIjIiwiIikgJT4lICNnZXQgcmlkIG9mIHRoZSBwb3VuZCBzaWduCiAgc3RyX3NwbGl0KHBhdHRlcm4gPSAiXHQiKSAlPiUgI3NwbGl0IG9uIHRoZSB0YWJzCiAgbWFncml0dHI6OmV4dHJhY3QyKDEpCnZjZi5oZWFkZXIKYGBgCgpnZXQgdGhlIGRhdGEKYGBge3J9CnNucHMgPC0gcmVhZF90c3YoIi4uL2lucHV0L0xULnZjZi5neiIsIG5hID0gYygiIiwiTkEiLCIuIiksY29tbWVudD0iIyIsY29sX25hbWVzID0gdmNmLmhlYWRlcikgJT4lCiAgc2VsZWN0KC1JRCwgLUZJTFRFUikgIyB0aGVzZSBhcmUgZW1wdHkgY29sdW1ucwpzbnBzCmBgYAoKZmlsdGVyIHRvIGtlZXAgc25wcyB3aGVyZSB0aGVyZSBpcyBkYXRhIGZyb20gYWxsIHNhbXBsZXMKYGBge3J9CnNucHMgPC0gc25wcyAlPiUgCiAgZmlsdGVyKHtzZWxlY3QoLiwgbWF0Y2hlcygiWzAtOV0iKSkgJT4lIGNvbXBsZXRlLmNhc2VzKCkgfSkKc25wcwpgYGAKCmBgYHtyfQpzbnBzIDwtIHNucHMgJT4lIAogIG11dGF0ZShUT1RBTF9ERVBUSD0ge3N0cl9leHRyYWN0KElORk8sICJEUD1bMC05XSoiKSAlPiUgCiAgICAgIHN0cl9yZW1vdmUoIkRQPSIpICU+JQogICAgICBhcy5udW1lcmljKCkgfQogICkgJT4lCiAgZmlsdGVyKFFVQUwgPj0xMDAsCiAgICAgICAgIG5jaGFyKEFMVCk9PTEsCiAgICAgICAgIFRPVEFMX0RFUFRIID4gcXVhbnRpbGUoVE9UQUxfREVQVEgsIDAuMDUpLAogICAgICAgICBUT1RBTF9ERVBUSCA8IHF1YW50aWxlKFRPVEFMX0RFUFRILCAwLjk1KSkKc25wcwpgYGAKCnVucGFjayB0aGUgaW5mb3JtYXRpb24gZGlmZmVybmV0IHNhbXBsZXM6CgpgYGB7cn0Kc2FtcGxlcyA8LSBjb2xuYW1lcyhzbnBzKSAlPiUgc3RyX3N1YnNldCgiXlswLTldIikKCmZvciAocyBpbiBzYW1wbGVzKSB7CnNucHMgPC0gc25wcyAlPiUKICBzZXBhcmF0ZSghIXMsIGludG89cGFzdGUocyxjKCJndCIsInRvdC5kZXB0aCIsImFsbGVsZS5kZXB0aCIsInJlZi5kZXB0aCIsInJlZi5xdWFsIiwiYWx0LmRlcHRoIiwiYWx0LnF1YWwiLCJndC5saWsiKSxzZXA9Il8iKSwKICAgICAgICAgICBzZXA9IjoiLCBjb252ZXJ0ID0gVFJVRSkKfQpzbnBzCmBgYAoKRm9yIHRoZSBQQ0Egd2Ugb25seSBuZWVkIHRoZSBnZW5vdHlwZSBpbmZvCmBgYHtyfQpndHMgPC0gc25wcyAlPiUKICBzZWxlY3QoQ0hST00sIFBPUywgZW5kc193aXRoKCJfZ3QiKSkKZ3RzCmBgYAoKcmVtb3ZlIHRoZSAzOExUUiBnYW1wbGUKCmBgYHtyfQpndHMgPC0gZ3RzICU+JSBzZWxlY3QoLWAzOExUUl9ndGApCmBgYAoKCmNvbnZlcnQgdGhpcyB0byBudW1lcmljCmBgYHtyfQpnZW5vLm51bWVyaWMgPC0gZ3RzICU+JQogIHNlbGVjdCgtQ0hST00sIC1QT1MpICU+JQogIGxhcHBseShmYWN0b3IpICU+JSAjIGNvbnZlcnQgY2hhcmN0ZXJzIHRvICJmYWN0b3JzIiwgd2hlcmUgZWFjaCBjYXRlZ29yeSBpcyBpbnRlcm5hbGx5IHJlcHJlc2VudGVkIGFzIGEgbnVtYmVyCiAgYXMuZGF0YS5mcmFtZSgpICU+JSAjIHJlZm9ybWF0CiAgZGF0YS5tYXRyaXgoKSAlPiUjICBjb252ZXJ0IHRvIG51bWVyaWMKICB0KCkgCgpjb2xuYW1lcyhnZW5vLm51bWVyaWMpIDwtIHN0cl9jKGd0cyRDSFJPTSwgIl8iLCBndHMkUE9TKQoKaGVhZChnZW5vLm51bWVyaWNbLDE6NV0sMTApCgpkaW0oZ2Vuby5udW1lcmljKQpkaW0oZ3RzKQpgYGAKCmdldCB0aGUgcHJpbmNpcGFsIGNvbXBvbmVudHMKCmBgYHtyfQpwY2EgPC0gcHJjb21wKGdlbm8ubnVtZXJpYykKc3VtbWFyeShwY2EpCmBgYAoKcGxvdCBpdApgYGB7cn0KcGxvdC5kYXRhIDwtIHBjYSR4ICU+JQogIGFzLmRhdGEuZnJhbWUgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCJzYW1wbGUiKSAlPiUKICBtdXRhdGUocmVzcG9uc2U9c3RyX2V4dHJhY3Qoc2FtcGxlLCAiKExUUnxMVFdSKSIpKSAlPiUKICBnYXRoZXIoa2V5PSJjb21wb25lbnQiLCB2YWx1ZT0idmFsdWUiLFBDMjpQQzkpCiAgCnBsb3RzIDwtIG1hcChzb3J0KHVuaXF1ZShwbG90LmRhdGEkY29tcG9uZW50KSksIGZ1bmN0aW9uKHgpIHsKICBwbG90LmRhdGEgJT4lCiAgICBmaWx0ZXIoY29tcG9uZW50PT14KSAlPiUKICAgIGdncGxvdChhZXMoeD1QQzEsIHk9IHZhbHVlLCBsYWJlbD1zYW1wbGUsIGNvbG9yPXJlc3BvbnNlKSkgKwogICAgICBnZW9tX3BvaW50KCkgKyB5bGFiKHgpCiAgfQogICkKCnBsb3RzICAKYGBgCgojIyBzbnBzIHRoYXQgY29udHJpYnV0ZSB0aGUgbW9zdCB0byBQQzE6CgpgYGB7cn0KbG9hZGluZ3MgPC0gYXMuZGF0YS5mcmFtZShwY2Ekcm90YXRpb24pICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigic25wIikgJT4lCiAgc2VsZWN0KFBDMSwgc25wKSAlPiUKICBhcnJhbmdlKGRlc2MoYWJzKFBDMSkpKQpsb2FkaW5ncwpgYGAKCmBgYHtyfQpjb250cmlidXRpb25zIDwtIGxvYWRpbmdzICU+JQogIHNlcGFyYXRlKHNucCwgaW50bz1jKCJjb250aWciLCAicG9zIiksIHNlcD0iXyIpICU+JQogIGdyb3VwX2J5KGNvbnRpZykgJT4lCiAgc3VtbWFyaXplKGFicy5jb250cmlidXRpb24gPSBhYnMoc3VtKFBDMSkpLAogICAgICAgICAgICBjb250cmlidXRpb24gPSBzdW0oUEMxKSwKICAgICAgICAgICAgbnVtYmVyX29mX3NucHMgPSBuKCkpICU+JQogIGFycmFuZ2UoZGVzYyhhYnMuY29udHJpYnV0aW9uKSkKY29udHJpYnV0aW9ucwpgYGAKCmJyaW5nIGluIHNlcSBsZW5ndGhzCgpgYGB7cn0KbGVuZ3RocyA8LSByZWFkX2NzdigiLi4vaW5wdXQvUGluc2Fwb3JlZmVyZW5jZTFfbGVuZ3Rocy5jc3YiLCBjb2xfbmFtZXMgPSBjKCJjb250aWciLCAibGVuZ3RoIiksIHNraXA9MSkgJT4lCiAgbXV0YXRlKGNvbnRpZyA9IHN0cl9yZW1vdmUoY29udGlnLCAiIC4qIikpCmxlbmd0aHMKYGBgCgpgYGB7cn0KY29udHJpYnV0aW9ucyA8LSBjb250cmlidXRpb25zICU+JSAKICBsZWZ0X2pvaW4obGVuZ3RocykgJT4lCiAgbXV0YXRlKHNucHNfcGVyXzEwMGJwID0gcm91bmQobnVtYmVyX29mX3NucHMgLyBsZW5ndGggKiAxMDAsIDIpKSAlPiUKICBzZWxlY3QoY29udGlnLCBjb250cmlidXRpb24sIGxlbmd0aCwgbnVtYmVyX29mX3NucHMsIHNucHNfcGVyXzEwMGJwKQpjb250cmlidXRpb25zCmBgYAoKYGBge3J9CndyaXRlX2Nzdihjb250cmlidXRpb25zLCAiLi4vb3V0cHV0L2dlbmVfY29udHJpYnV0aW9ucy5jc3YuZ3oiKQpgYGAKCiMjIG1ha2UgcGxvdCBvZiBQQzEgdnMgUEMyIGZvciBwYXBlcgoKYGBge3J9CnBjMXBjMiA8LSBwY2EkeCAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCJJRCIpICU+JQogIHNlbGVjdChJRCwgUEMxLCBQQzIpICU+JQogIG11dGF0ZShJRD17c3RyX3JlcGxhY2UoSUQsICJXIiwgIk4iKSAlPiUKICAgICAgIHN0cl9yZXBsYWNlKCJSUiIsICJSMiIpICU+JQogICAgICBzdHJfcmVtb3ZlX2FsbCgiKFh8X2d0KSIpIH0sCiAgICByZXNwb25zZT1pZmVsc2Uoc3RyX2RldGVjdChJRCwiTiIpLCAibm8gcmVjb3ZlcnkiLCAicmVjb3ZlcnkiKSkKcGMxcGMyCmBgYAoKYGBge3J9CnBjMXBjMiAlPiUKICBnZ3Bsb3QoYWVzKHg9UEMxLCB5ID0gUEMyLCBsYWJlbD1JRCwgY29sb3I9cmVzcG9uc2UpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3RleHRfcmVwZWwoc2hvdy5sZWdlbmQ9RkFMU0UsIGRpcmVjdGlvbj0ieSIpCmdnc2F2ZSgiLi4vb3V0cHV0L1BDQS5wZGYiKQpgYGAKCkNyZWF0ZSBhIGxpc3Qgb2YgR0NaTjAxMDU0MTU4LjEgU05QcwpgYGB7cn0KR0NaTjAxMDU0MTU4LjEubG9hZGluZ3MgPC0gbG9hZGluZ3MgJT4lCiAgZmlsdGVyKHN0cl9kZXRlY3Qoc25wLCBmaXhlZCgiR0NaTjAxMDU0MTU4LjEiKSkpICU+JQogIHNlcGFyYXRlKHNucCxpbnRvID0gYygiY29udGlnIiwgInBvc2l0aW9uIiksIHNlcD0iXyIsY29udmVydCA9IFRSVUUpICU+JQogIGFycmFuZ2UocG9zaXRpb24pCkdDWk4wMTA1NDE1OC4xLmxvYWRpbmdzCmBgYAoKYGBge3J9CkdDWk4wMTA1NDE1OC5zbnBpbmZvIDwtIGxlZnRfam9pbihHQ1pOMDEwNTQxNTguMS5sb2FkaW5ncywgc25wcywgYnk9YygiY29udGlnIiA9ICJDSFJPTSIsICJwb3NpdGlvbiIgPSAiUE9TIikpCkdDWk4wMTA1NDE1OC5zbnBpbmZvCmBgYAoKYGBge3J9CndyaXRlX2NzdihHQ1pOMDEwNTQxNTguc25waW5mbywgIi4uL291dHB1dC9HQ1pOMDEwNTQxNTguc25waW5mby5jc3YiKQpgYGAKCkNyZWF0ZSBhIGxpc3Qgb2YgU05QcyBpbiBhbGwgZ2VuZXMgd2l0aCBmaXhlZCBkaWZmZXJlbmNlcwoKYGBge3J9CmdldF9zbnBzIDwtIGZ1bmN0aW9uKGNvbnRpZywgbG9hZGluZ3M9bG9hZGluZ3MpIHsKICBsb2FkaW5ncyAlPiUKICBmaWx0ZXIoc3RyX2RldGVjdChzbnAsIGZpeGVkKCJHQ1pOMDEwNTQxNTguMSIpKSkgJT4lCiAgc2VwYXJhdGUoc25wLGludG8gPSBjKCJjb250aWciLCAicG9zaXRpb24iKSwgc2VwPSJfIixjb252ZXJ0ID0gVFJVRSkgJT4lCiAgYXJyYW5nZShwb3NpdGlvbikKR0NaTjAxMDU0MTU4LjEubG9hZGluZ3MKR0NaTjAxMDU0MTU4LnNucGluZm8gPC0gbGVmdF9qb2luKEdDWk4wMTA1NDE1OC4xLmxvYWRpbmdzLCBzbnBzLCBieT1jKCJjb250aWciID0gIkNIUk9NIiwgInBvc2l0aW9uIiA9ICJQT1MiKSkKR0NaTjAxMDU0MTU4LnNucGluZm8KfQpgYGAKCgpgYGB7cn0KZml4ZWRfZ2VuZXMgPC0gcmVhZF9jc3YoIi4uL291dHB1dC9maXhlZF9zbnBzLmNzdiIpCmZpeGVkX2dlbmVzCmBgYAoKYGBge3J9CmZpeGVkX2dlbmVzX3NucHMgPC0gbG9hZGluZ3MgJT4lIAogIHNlcGFyYXRlKHNucCxpbnRvID0gYygiY29udGlnIiwgInBvc2l0aW9uIiksIHNlcD0iXyIsY29udmVydCA9IFRSVUUsIHJlbW92ZSA9IEZBTFNFKSAlPiUKICBzZW1pX2pvaW4oZml4ZWRfZ2VuZXMsIGJ5PSJjb250aWciKSAlPiUKICBsZWZ0X2pvaW4oc25wcywgYnk9YygiY29udGlnIiA9ICJDSFJPTSIsICJwb3NpdGlvbiIgPSAiUE9TIikpICU+JQogIGFycmFuZ2UoY29udGlnLCBwb3NpdGlvbikKZml4ZWRfZ2VuZXNfc25wcwpgYGAKCmBgYHtyfQp3cml0ZV9jc3YoZml4ZWRfZ2VuZXNfc25wcywgIi4uL291dHB1dC9maXhlZF9nZW5lc19zbnBzLmNzdiIpCmBgYAoK